# 一、导入数据相关包
import warnings

import lightgbm as lgb
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from imblearn.over_sampling import SMOTE
from sklearn import metrics
from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from xgboost import plot_importance
from xgboost.sklearn import XGBClassifier

# 二、分析目的
"""
客户流失分析的主要目的是通过分析用户特征，寻找对用户流失影响较大的用户特征。
根据电商领域的业务知识，提出产品/平台的运营建议，以提高用户粘性，降低用户流失率。
"""
"""
1、流失用户有哪些属性？
2、尝试找到合适的模型预测流失用户。
3、针对性给出增加用户黏性、预防流失的建议。
"""

# 忽略警告提示
warnings.filterwarnings('ignore')

# 用黑体显示中文
plt.rcParams['font.sans-serif'] = ['SimHei']

# 加载数据集
df = pd.read_excel('E:\桌面\大四\毕设\Project Dataset.xlsx', sheet_name='E Comm')
df.shape

# 查看数据信息
df.head()

# 数据集描述
df.info()
# 根据上面打印的结果，我们发现数据总共有5630行，共有19个变量。


# 三、数据清洗
# （一）判断某指标的缺失值
missingTotal = df.isnull().sum()
missingExist = missingTotal[missingTotal > 0]
missingExist = missingExist.sort_values(ascending=False)
print(missingTotal)
print(missingExist)

# （二）处理缺失值
# 1、使用中位数填充具有明显离群值和偏态分布的数据
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
plt.title('全部用户的任期直方图')
sns.distplot(df['Tenure'].dropna(), ax=axes[0])
plt.title('流失用户的任期直方图')
sns.distplot(df[df['Churn'] == 1]['Tenure'].dropna(), ax=axes[1])
plt.title('留存用户的任期直方图')
sns.distplot(df[df['Churn'] == 0]['Tenure'].dropna(), ax=axes[2])
plt.show()

df['Tenure'] = df['Tenure'].fillna(df['Tenure'].median())  # 数据有明显的离群值，切呈偏态分布
df['WarehouseToHome'] = df['WarehouseToHome'].fillna(df['WarehouseToHome'].median())  # 数据有明显离群值
df['OrderAmountHikeFromlastYear'] = df['OrderAmountHikeFromlastYear'].fillna(
    df['OrderAmountHikeFromlastYear'].median())  # 数据呈偏态分布
df['CouponUsed'] = df['CouponUsed'].fillna(df['CouponUsed'].median())  # 数据呈偏态分布
df['DaySinceLastOrder'] = df['DaySinceLastOrder'].fillna(df['DaySinceLastOrder'].median())  # 数据有明显离群值，且呈偏态分布

# 2、使用均值填充变量值较好地集中在均值周围的数据
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
plt.title('全部用户的app花费时间直方图')
sns.distplot(df['HourSpendOnApp'].dropna(), ax=axes[0])
plt.title('流失用户的app花费时间直方图')
sns.distplot(df[df['Churn'] == 1]['HourSpendOnApp'].dropna(), ax=axes[1])
plt.title('留存用户的app花费时间直方图')
sns.distplot(df[df['Churn'] == 0]['HourSpendOnApp'].dropna(), ax=axes[2])
plt.show()

df['HourSpendOnApp'] = df['HourSpendOnApp'].fillna(df['HourSpendOnApp'].mean())

# 3、使用特殊值填充缺失值  数据的空值代表上月无订单，填充为0
df['OrderCount'] = df['OrderCount'].fillna('0')

# 4、再次判断缺失值
missingTotal = df.isnull().sum()
missingExist = missingTotal[missingTotal > 0]
missingExist = missingExist.sort_values(ascending=False)
print(missingTotal)
print(missingExist)
# 此时数据中已经无缺失值

# （三）数据一致化
# 目的是对数据进行相关处理，以便后续计算分析。

# 将 Churn 列转换为整数类型
df['Churn'] = df['Churn'].astype(int)

# 将 tenure 列的数据类型转换为整数
df['Tenure'] = df['Tenure'].astype(int)

# 将 ageGroup 列的数据类型转换为整数
df['AgeGroup'] = df['AgeGroup'].astype(int)

# 将性别属性转换为 0 和 1，女性为 0，男性为 1
# df['Gender'] = pd.factorize(df['Gender'])[0]

# 将 HourSpendOnApp 列的数据类型转换为浮点数
df['HourSpendOnApp'] = df['HourSpendOnApp'].astype(float)

# 将 OrderCount 列的数据类型转换为整数
df['OrderCount'] = df['OrderCount'].astype(int)

# 将 OrderAmountHikeFromlastYear 列的数据类型转换为浮点数
df['OrderAmountHikeFromlastYear'] = df['OrderAmountHikeFromlastYear'].astype(float)

# 将 DaySinceLastOrder 列的数据类型转换为整数
df['DaySinceLastOrder'] = df['DaySinceLastOrder'].astype(int)

# 将 PreferredOrderCat 列的数据类型转换为字符串
df['PreferedOrderCat'] = df['PreferedOrderCat'].astype(str)

# 将 NumberOfStreamerFollowed 列的数据类型转换为整数
df['NumberOfStreamerFollowed'] = df['NumberOfStreamerFollowed'].astype(int)

# 将 SatisfactionScore 列的数据类型转换为浮点数
df['SatisfactionScore'] = df['SatisfactionScore'].astype(float)

# 将 Complain 列的数据类型转换为整数
df['Complain'] = df['Complain'].astype(int)

# 将 DiscountAmount 列的数据类型转换为浮点数
df['DiscountAmount'] = df['DiscountAmount'].astype(float)

# 将 DiscountAmount 列的数据类型转换为浮点数
df['DiscountAmount'] = df['DiscountAmount'].astype(float)

# 四、用户特征探索性分析
# （一）
# 查看流失用户数量和占比。
plt.figure(figsize=(10, 10))
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用黑体显示中文
# 数据
sizes = df['Churn'].value_counts()
labels = df['Churn'].value_counts().index
# 画图
plt.pie(sizes, explode=(0.1, 0), labels=labels, colors=['#4682B4', '#ADD8E6'], autopct='%1.1f%%', shadow=True)
plt.title('客户流失率')
# 展示
plt.show()
# 流失用户占比16.8%，留存用户占比83.2%，属于不平衡数据集。

# （二）用户维度特征分析
data = df.copy()
data.head()

# 1、常用登陆设备分析
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(data=data, x='PreferredLoginDevice', hue='Churn', palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='PreferredLoginDevice', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 上图中明显看出，使用Mobile Phone的用户流失数量最多，但是使用Phone的用户流失率最高。

# 定义常用登陆设备分析函数
def data_devices_analysis(df):
    login_devices = df['PreferredLoginDevice']
    login_devices_unique = login_devices.unique()

    # 提取流失用户和非流失用户的登陆设备
    churned_users = df[df['Churn'] == 1]['PreferredLoginDevice']
    churned_users_unique = churned_users.unique()

    non_churned_users = df[df['Churn'] == 0]['PreferredLoginDevice']
    non_churned_users_unique = non_churned_users.unique()

    # 统计每种登陆设备的用户数量
    device_counts_churned = pd.Series(churned_users).value_counts()  # 总数
    device_counts_non_churned = pd.Series(non_churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(device_counts_churned, labels=churned_users_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Login Devices of Churned Users')
    plt.show()
    # 保存图表为图片
    # plt.savefig('./Main/picture/Login Devices of Churned Users.png', bbox_inches='tight')

    plt.figure(figsize=(10, 6))
    plt.pie(device_counts_non_churned, labels=non_churned_users_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Login Devices of Non-Churned Users')
    plt.show()
    # plt.savefig('./Main/picture/Login Devices of Non_Churned Users.png', bbox_inches='tight')

    # 2）统计每种登陆设备的使用数量
    device_counts = pd.Series(login_devices).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(device_counts, labels=login_devices_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Login Devices of All Users')
    plt.show()
    # plt.savefig('./Main/picture/Login Devices of All Users.png', bbox_inches='tight')

    # 3）计算不同登陆设备的用户流失率
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, fc='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, bottom=churned_users_rate, fc='b', alpha=0.5)
    # 添加百分比标签
    for i in range(len(churned_users_unique)):
        plt.text(i, churned_users_rate[i] + non_churned_users_rate[i],
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center')
    plt.title('Churn Users and No Churn Users Percentages by Login Device')
    plt.xlabel('Login Device')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'])
    plt.show()
    # plt.savefig('./Main/picture/Churn Users and No Churn Users Percentages by Login Device.png', bbox_inches='tight')


# 主函数
if __name__ == '__main__':
    df = df
    data_devices_analysis(df)

# 结论指出，使用 Mobile Phone 的总用户数最多，但 Pad 和 Phone 的流失用户占比较高。
# 建议产品团队测试 Pad 和 Phone 终端的产品稳定性。

# 2、城市等级分析
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(data=data, x='CityTier', hue='Churn', palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='CityTier', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 上图中明显看出，1级城市流失人数最多，但是3级城市的用户流失率更高。

# 定义城市等级分析函数：
def data_city_analysis(df):
    city_tiers = df['CityTier']
    city_tiers_unique = city_tiers.unique()

    # 统计每种城市等级的用户数量
    city_counts = pd.Series(city_tiers).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(city_counts, labels=city_tiers_unique, autopct='%1.1f%%', startangle=90)
    plt.title('City Tiers')
    plt.show()

    # 2）城市等级与流失用户分析
    churned_users = df[df['Churn'] == 1]['CityTier']
    churned_counts = pd.Series(churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(churned_counts, labels=city_tiers_unique, autopct='%1.1f%%', startangle=90)
    plt.title('City Tiers of Churned Users')
    plt.show()

    # 3）城市等级与非流失用户分析
    non_churned_users = df[df['Churn'] == 0]['CityTier']
    non_churned_counts = pd.Series(non_churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(non_churned_counts, labels=city_tiers_unique, autopct='%1.1f%%', startangle=90)
    plt.title('City Tiers of Non-Churned Users')
    plt.show()

    # 4）城市等级整体分析
    churned_users_unique = churned_users.unique()
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_users_unique = non_churned_users.unique()
    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, color='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, bottom=churned_users_rate, color='b', alpha=0.5)
    # 添加百分比标签
    for i in range(1, len(churned_users_unique) + 1):
        plt.text(i, churned_users_rate[i] + 0.1,
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center')
    plt.title('Churn Users and No Churn Users Percentages by City Tiers')
    plt.xlabel('City Tiers')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'], loc='upper center')
    plt.show()


# 主函数
if __name__ == '__main__':
    df = df
    data_city_analysis(df)

# 结论：城市等级为3的流失用户占比为57.3%，城市等级为2的流失用户占比为55.0%，远高于城市等级为1的45.6%。
# 建议运营团队在城市等级为2和3的城市适当开展运营活动（如鼓励城市特色作品直播等），提高用户粘性。

# 3、性别分析
plt.rcParams['font.size'] = '16'
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(x='Gender', hue='Churn', data=data, palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='Gender', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 从上图中明显看出，性别与用户流失有差异，男性流失数量更高一些，并且流失率也更高。

# 定义性别分析函数
def data_gender_analysis(df):
    genders = df['Gender']
    genders_unique = genders.unique()

    # 统计每种性别的用户数量
    gender_counts = pd.Series(genders).value_counts()

    # 绘制饼图
    plt.figure()
    plt.pie(gender_counts, labels=genders_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Gender')
    plt.show()

    # 2）性别与流失用户分析
    churned_users = df[df['Churn'] == 1]['Gender']
    churned_counts = pd.Series(churned_users).value_counts()

    # 绘制饼图
    plt.figure()
    plt.pie(churned_counts, labels=genders_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Gender of Churned Users')
    plt.show()

    # 3）性别与非流失用户分析
    non_churned_users = df[df['Churn'] == 0]['Gender']
    non_churned_counts = pd.Series(non_churned_users).value_counts()

    # 绘制饼图
    plt.figure()
    plt.pie(non_churned_counts, labels=genders_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Gender of Non-Churned Users')
    plt.show()

    # 4）性别整体分析
    churned_users_unique = churned_users.unique()
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_users_unique = non_churned_users.unique()
    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, color='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, color='b', alpha=0.5)
    # 添加百分比标签
    for i in range(len(churned_users_unique)):
        plt.text(i, churned_users_rate[i],
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center',
                 va='bottom', fontsize=15)
    plt.title('Churn Users and No Churn Users Percentages by Gender')
    plt.xlabel('Gender')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'])
    plt.show()


# 主函数
if __name__ == '__main__':
    df = df
    data_gender_analysis(df)

# 结论：女性用户是平台的主要用户，女性的流失用户占比为47.5%，男性的流失用户占比为51.6%，基本持平。
# 建议运营团队根据男性与女性喜欢的直播风格，进行直播内容定向推送，尝试降低其流失率。

# 4、年龄分析
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(data=data, x='AgeGroup', hue='Churn', palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='AgeGroup', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 从上图明显看出，相较于其他年龄段的用户而言，60岁以上的老年人用户数量较少，但老年用户的流失率要远远高于其他年龄用户。

# 定义年龄分析函数
def data_ages_analysis(df):
    ages = df['AgeGroup']
    ages_unique = ages.unique()

    # 统计每种年龄的用户数量
    ages_counts = pd.Series(ages).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(ages_counts, labels=ages_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Age Groups')
    plt.show()

    # 2）年龄与流失用户分析
    churned_users = df[df['Churn'] == 1]['AgeGroup']
    churned_counts = pd.Series(churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(churned_counts, labels=ages_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Age Groups of Churned Users')
    plt.show()

    # 3）年龄与非流失用户分析
    non_churned_users = df[df['Churn'] == 0]['AgeGroup']
    non_churned_counts = pd.Series(non_churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(non_churned_counts, labels=ages_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Age Groups of Non-Churned Users')
    plt.show()

    # 4）年龄整体分析
    churned_users_unique = churned_users.unique()
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_users_unique = non_churned_users.unique()
    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, color='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, color='b', alpha=0.5)
    # 添加百分比标签
    for i in range(1, len(churned_users_unique) + 1):
        plt.text(i, non_churned_users_rate[i],
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center')
    plt.title('Churn Users and No Churn Users Percentages by Age Groups')
    plt.xlabel('Age Groups')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'])
    plt.show()


# 主函数
if __name__ == '__main__':
    df = df
    data_ages_analysis(df)

# 结论：年龄分组为6的流失用户占比最高，为72.3%，其次是年龄分组为5，流失用户占比为58.9%。
# 建议运营团队鼓励更多年龄分组为5和6喜欢的直播内容和商品进驻，提高其留存率。

# 5、婚姻状况分析
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(data=data, x='MaritalStatus', hue='Churn', palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='MaritalStatus', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 上图中明显看出，单身的用户中流失人数最多，并且流失率最高。

# 定义婚姻状况函数
def data_matrital_analysis(df):
    marital_statuses = df['MaritalStatus']
    marital_statuses_unique = marital_statuses.unique()

    # 统计每种婚姻状况的用户数量
    marital_counts = pd.Series(marital_statuses).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(marital_counts, labels=marital_statuses_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Marital Status')
    plt.show()

    # 2）婚姻状况与流失用户分析
    churned_users = df[df['Churn'] == 1]['MaritalStatus']
    churned_counts = pd.Series(churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(churned_counts, labels=marital_statuses_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Marital Status of Churned Users')
    plt.show()

    # 3）婚姻状况与非流失用户分析
    non_churned_users = df[df['Churn'] == 0]['MaritalStatus']
    non_churned_counts = pd.Series(non_churned_users).value_counts()

    # 绘制饼图
    plt.figure(figsize=(10, 6))
    plt.pie(non_churned_counts, labels=marital_statuses_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Marital Status of Non-Churned Users')
    plt.show()

    # 4）婚姻状况整体分析
    churned_users_unique = churned_users.unique()
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_users_unique = non_churned_users.unique()
    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, color='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, color='b', alpha=0.5)
    # 添加百分比标签
    for i in range(len(churned_users_unique)):
        plt.text(i, churned_users_rate[i] + 0.05,
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center')
    plt.title('Churn Users and No Churn Users Percentages by Marital Status')
    plt.xlabel('Marital Status')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'])
    plt.show()


# 主函数
if __name__ == '__main__':
    df = df
    data_matrital_analysis(df)

# 结论：单身的总用户数最多，且流失用户占比较低，是产品的优质用户群，离异的流失用户占比较高，为56.4%，远高于单身和已婚的用户。
# 建议运营团队在挖掘新用户时，更注意去吸引单身的用户，对于现有的离异用户，采取积极留存措施。

# 6、上月首选订单类型分析
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
sns.countplot(data=data, x='PreferedOrderCat', hue='Churn', palette='Blues_r', ax=axes[0])
sns.barplot(data=data, x='PreferedOrderCat', y='Churn', palette='Blues_r', ax=axes[1])
plt.show()


# 上图中明显看出，Mobile Phone的流失人数最多，并且用户流失率也最高。

# 定义上月首选订单类型分析函数
def data_preferedordercat_analysis(df):
    order_cats = df['PreferedOrderCat']
    order_cats_unique = order_cats.unique()

    # 统计每种上月首选订单类型的用户数量
    cat_counts = pd.Series(order_cats).value_counts()

    # 绘制饼图展示上月首选订单类型分布
    plt.figure(figsize=(10, 6))
    plt.pie(cat_counts, labels=order_cats_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Order Cats')
    plt.show()

    # 2）上月订单情况与流失用户分析
    churned_users = df[df['Churn'] == 1]['PreferedOrderCat']
    churned_counts = pd.Series(churned_users).value_counts()

    # 绘制饼图示上月流失用户的订单类型分布
    plt.figure(figsize=(10, 6))
    plt.pie(churned_counts, labels=order_cats_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Order Cats of Churned Users')
    plt.show()

    # 3）上月订单情况与非流失用户分析
    non_churned_users = df[df['Churn'] == 0]['PreferedOrderCat']
    non_churned_counts = pd.Series(non_churned_users).value_counts()

    # 绘制饼图展示上月非流失用户的订单类型分布
    plt.figure(figsize=(10, 6))
    plt.pie(non_churned_counts, labels=order_cats_unique, autopct='%1.1f%%', startangle=90)
    plt.title('Order Cats of Non-Churned Users')
    plt.show()

    # 4）上月订单情况整体分析
    churned_users_unique = churned_users.unique()
    churned_counts = churned_users.value_counts()
    churned_users_rate = churned_counts / churned_counts.sum()

    non_churned_users_unique = non_churned_users.unique()
    non_churned_counts = non_churned_users.value_counts()
    non_churned_users_rate = non_churned_counts / non_churned_counts.sum()

    # 绘制柱状图展示上月不同订单类型的用户流失率
    plt.figure(figsize=(10, 6))
    plt.bar(churned_users_unique, churned_users_rate, color='r', alpha=1)
    plt.bar(non_churned_users_unique, non_churned_users_rate, color='b', alpha=0.5)
    # 添加百分比标签
    for i in range(len(churned_users_unique)):
        plt.text(i, non_churned_users_rate[i],
                 f'{churned_users_rate[i] / (churned_users_rate[i] + non_churned_users_rate[i]) * 100}%', ha='center')
    plt.title('Churn Users and No Churn Users Percentages by Order Cats of Churned Users')
    plt.xlabel('Order Cats of Churned Users')
    plt.ylabel('Churn Rate')
    plt.legend(['Churn Users', 'No Churn Users'])
    plt.show()


if __name__ == '__main__':
    df = df
    data_preferedordercat_analysis(df)

# 结论：月主要订单为Mobile Phone和Household的流失用户占比较多，均超过了50%，其次是上月订单为Others的用户。
# 可能是由于Mobile Phone和Household的商品使用周期较长，用户购买该类商品后，很长一段时间不再有相同的购买意愿，从而造成用户流失。

# 7、使用时长对流失的影响
# HourSpendOnApp:用户使用时长,单位(小时)
plt.figure(figsize=(9, 4))
g = sns.kdeplot(data['HourSpendOnApp'][(data['Churn'] == 0)], color='green', shade=True)
g = sns.kdeplot(data['HourSpendOnApp'][(data['Churn'] == 1)], ax=g, color='orange', shade=True)
g.set_xlabel('HourSpendOnApp')
g.set_ylabel('Frequency')
plt.title('时长与流失的关系图')
g.legend(['未流失', '流失'])
plt.show()
# 从上图中明显看出，使用时长越久流失率越低，符合一般经验；流失率在3小时左右是最高的。
# 看来3小时是个很关键的分水岭，要注重这段时间用户的维护，以降低流失率。

## （二）用户行为特征分析

# 将性别属性转换为 0 和 1，女性为 0，男性为 1
df['Gender'] = pd.factorize(df['Gender'])[0]

# 提取各项特征和流失标签
features = df[['Tenure', 'WarehouseToHome', 'AgeGroup', 'Gender', 'HourSpendOnApp', 'OrderCount',
               'OrderAmountHikeFromlastYear', 'DaySinceLastOrder', 'CouponUsed',
               'NumberOfStreamerFollowed', 'SatisfactionScore', 'DiscountAmount']]
churn = df['Churn']

# 计算用户行为特征的平均值和标准差
average_values = features.mean()
std_values = features.std()

# 打印平均值和标准差
print('Average Values:')
print(average_values)
print()
print('Standard Deviation Values:')
print(std_values)

# 绘制箱线图
for feature in features.columns:
    plt.subplot(3, 4, features.columns.get_loc(feature) + 1)
    plt.boxplot([df[churn == 0][feature], df[churn == 1][feature]], labels=['non_Churned Users', 'Churned Users'])
    plt.title(f'Boxplot for {feature}')
    plt.xlabel('churned?')
    plt.ylabel('Values')
    plt.legend()
    plt.tight_layout()
    plt.show()

# 观察用户流失与各个维度之间的关系
df_onehot = pd.get_dummies(df.iloc[:, 1:19])
df_onehot.head()
plt.figure(figsize=(15, 4))
df_onehot.corr()['Churn'].sort_values(ascending=False).plot(kind='bar')
plt.title('用户流失与各个维度之间的关系')

## 五、用户特征相关性分析
# 1、计算特征的相关系数矩阵
features = df[['Tenure', 'WarehouseToHome', 'AgeGroup', 'Gender', 'HourSpendOnApp', 'OrderCount',
               'OrderAmountHikeFromlastYear', 'DaySinceLastOrder', 'CouponUsed',
               'NumberOfStreamerFollowed', 'SatisfactionScore', 'DiscountAmount']]
target = df['Churn']

correlation_coefficients = features.corrwith(churn)
print("用户流失相关系数: ")
print(correlation_coefficients)

# 2、 绘制相关系数矩阵图
correlation_matrix = features.corr()

plt.figure(figsize=(10, 6))
sns.heatmap(correlation_matrix, cmap='viridis')
plt.title('Correlation Matrix')
plt.show()

# 3、结论：
''' 
 Tenure（任期）与用户流失呈负相关，相关系数为 -0.315941。这表明任期较长的用户可能更不容易流失。
 WarehouseToHome与用户流失有一定的正相关性，系数为 0.069544。
 AgeGroup与用户流失呈正相关，说明不同年龄组可能对流失有一定影响。
 Gender与用户流失的相关性较小，为 0.029264。
 HourSpendOnApp与用户流失的相关性也较小，为 0.018126。
 OrderCount与用户流失呈负相关，但相关性较弱，为 -0.015327。
 OrderAmountHikeFromlastYear与用户流失呈负相关。
 DaySinceLastOrder与用户流失呈较强的负相关，这意味着距离上次订单的时间越长，用户越容易流失。
 CouponUsed与用户流失的相关性较弱，为 -0.001430。
 NumberOfStreamerFollowed与用户流失呈正相关。
 SatisfactionScore与用户流失呈正相关，说明用户满意度可能对流失有一定影响。
 DiscountAmount与用户流失呈较强的负相关。
'''

## 六、模型建立与优化
# （一）模型建立与模型评估
# 1、样本处理
features = df[['Tenure', 'WarehouseToHome', 'AgeGroup', 'Gender', 'HourSpendOnApp', 'OrderCount',
               'OrderAmountHikeFromlastYear', 'DaySinceLastOrder', 'CouponUsed',
               'NumberOfStreamerFollowed', 'SatisfactionScore', 'DiscountAmount']]
target = df['Churn']
target.value_counts()
# 可以看出样本明显不均衡，差别很大

# 过抽样处理样本不均衡问题
oversample = SMOTE(random_state=0)
X, y = oversample.fit_resample(features, target)
y.value_counts()

# 2、建立训练数据集和测试数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print('过抽样数据特征：', X.shape, '训练数据特征：', X_train.shape, '测试数据特征：', X_test.shape)

print('过抽样后数据标签：', y.shape, '训练数据标签：', y_train.shape, '测试数据标签：', y_test.shape)

# 3、选择机器学习算法训练模型
# 1）定义机器学习模型
models = [
    ['Logistic Regression', LogisticRegression()],  # 单模型
    ['DecisionTree', DecisionTreeClassifier(criterion='entropy', random_state=0)],
    ['Naive Bayes', GaussianNB()],
    ['K Nearest Neighbors', KNeighborsClassifier(n_neighbors=5)],
    ['Random Forest', RandomForestClassifier()],  # 双模型
    ['AdaBoost', AdaBoostClassifier()],
    ['GBDT', GradientBoostingClassifier()],
    ['LightGBM', lgb.LGBMClassifier()],
    ['XGB', XGBClassifier(eval_metric=['logloss', 'auc', 'error'])]
]

# 2）作出ROC曲线
Classify_result = []
names = []
prediction = []
for name, model in models:
    model = model
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    accuracy_score = metrics.accuracy_score(y_test, y_pred)
    precision_score = metrics.precision_score(y_test, y_pred)
    recall_score = metrics.recall_score(y_test, y_pred)
    fpr, tpr, _ = metrics.roc_curve(y_test, model.predict_proba(X_test)[:, 1])
    roc_auc = metrics.auc(fpr, tpr)

    # 绘制 ROC 曲线
    plt.figure()
    plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'{name} (area = {roc_auc:.2f})')
    plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver Operating Characteristic Curve')
    plt.legend(loc="lower right")
    plt.show()

    class_eva = pd.DataFrame([accuracy_score, precision_score, recall_score, roc_auc])
    Classify_result.append(class_eva)
    name = pd.Series(name)
    names.append(name)
    y_pred = pd.Series(y_pred)
    prediction.append(y_pred)

# 3）创建参数表格
names = pd.DataFrame(names)
names = names[0].tolist()
result = pd.concat(Classify_result, axis=1)
result.columns = names
result.index = ["accuracy_score", "precision_score", "recall_score", "roc_auc"]
result
# 由图可得：XGBoost算法的学习效果最好，ROC得分为0.995。因此我们选用此模型作为客户流失的预测模型，用于评估是否存在用户流失情况。
# 从整体上看，融合的多模型ROC得分比单模型算法高，学习效果比单模型好。


# （二）模型优化
# 1、选择学习效果最好的XGB模型，通过GridSearch网格搜索，找到其最优的模型参数
params = {"learning_rate": [0.01, 0.05, 0.1, 0.2], "n_estimators": [100, 200, 300], "max_depth": [5, 10, 15]}
gridxgbc = GridSearchCV(
    estimator=XGBClassifier(learning_rate=0.1, n_estimators=300, gamma=0, subsample=0.8, colsample_bytree=0.8,
                            nthread=1, scale_pos_weight=1, eval_metric='logloss', seed=27), param_grid=params, cv=5)
gridxgbc.fit(X_train, y_train)

# 2、打印最优参数和最优分数
print("Best parameters found: ", gridxgbc.best_params_)
print("Best score: {:.4f}".format(gridxgbc.best_score_))  # 0.9760
# 得到最优参数模型为：{'learning_rate': 0.1, 'max_depth': 10, 'n_estimators': 300}
# 即XGBoost模型中弱分类器的数量为300，最大树深度为10，最小叶子节点样本权重和为1

# 3、使用最优的模型参数，建立XGBoost模型，计算器ROC得分，并输出最优模型参数下的特征及特征重要度。
classifier = XGBClassifier(learning_rate=0.1, min_child_weight=1,
                           max_depth=10, n_estimators=300, gamma=0.4, subsample=0.9, colsample_bytree=0.6, nthread=1,
                           scale_pos_weight=1, eval_metric='logloss', seed=27)
classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)
recall = metrics.recall_score(y_test, y_pred)
precision = metrics.precision_score(y_test, y_pred)
roc_score = roc_auc_score(y_test, y_pred)
print("ROC 得分:", roc_score)  # 0.972

# 查看调参后
recall, precision
# 调参后XGBoost模型的精确率为97.1%、召回率为97.2%。

# 4、查看重要特征
plot_importance(classifier, height=0.5)
plt.show()
# 根据参数调优的XGB模型，对用户流失影响最大的用户特征依次为：上月平均折扣金额、仓库到顾客地址的距离、订单金额同比增长。
# 上述的用户行为特征与相关系数分析的略有差异，整体来说与探索性分析结果基本一致。

'''
建议：
用户方面：针对老年用户、无亲属、无伴侣用户的特征推出定制服务如老年朋友套餐、温暖套餐等。鼓励用户加强关联，推出各种亲子套餐、情侣套餐等，满足客户的多样化需求。针对新注册用户，推送半年优惠如赠送消费券，以度过用户流失高峰期。
优化折扣策略：可以评估和优化折扣策略，确保其既能吸引用户，又不会对企业利润造成过大影响。可适当开展补贴活动，鼓励商家对消费者进行补贴。
考虑距离因素：仓库到顾客地址的距离可能影响用户的满意度和忠诚度。可以优化物流配送策略，减少运输时间和成本，提高用户体验；增加发货仓库，降低商品的运输时间。
关注订单金额增长：对于订单金额同比增长，可以进一步分析增长的原因，并制定相应的营销策略来促进增长。
了解流媒体关注度的影响：探索如何利用用户关注的流媒体数量来提高用户参与度和留存率。
鼓励更多主播创造更多优质直播内容，鼓励用户收藏喜欢的主播，鼓励用户成为主播；
适当开展签到、每日任务等活动，提高用户使用平台时间，增强用户粘性；
对于长时间未下单的用户，平台对其进行精准消息推送，尝试召回用户。
'''
